/*
 *  Copyright 2008 The MITRE Corporation (http://www.mitre.org/). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.mitre.lattice.graph;
import org.jgap.Chromosome;
import org.jgap.Configuration;
import org.jgap.FitnessFunction;
import org.jgap.Gene;
import org.jgap.Genotype;
import org.jgap.InvalidConfigurationException;
import org.jgap.impl.DefaultConfiguration;
import org.jgap.impl.IntegerGene;
import org.mitre.lattice.lattice.LatticeFunctions;
import org.mitre.lattice.lattice.LatticeTree;
import org.mitre.mrald.util.Config;
import org.mitre.mrald.util.MraldOutFile;
/**
 *@author     jchoyt
 *@created    November 12, 2003
 */
public class MinimizingDistance {
    /**
     *  The total number of times we'll let the population evolve.
     */
    private final static int MAX_ALLOWED_EVOLUTIONS = 50;
    private static int maxDistance = 0;
    private static LatticeTree latticeTree = null;

    /**
     *  Constructor for the MinimizingDistance object
     */
    public MinimizingDistance() {
        try {

        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     *
     *@param  args  the command-line arguments.
     */
    public static void main(String[] args) {
        try {
            LatticeTree newLattice = (LatticeTree)GraphUserLattice.getLattice();
	    setLatticeTree(newLattice);
            calcDistance();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     *  Executes the genetic algorithm to determine the minimum number of coins
     *  necessary to make up the given target amount of change. The solution will
     *  then be written to System.out.
     *
     *@throws  Exception
     */
    public static Object[] calcDistance()
        throws Exception {
        //Unserialize the sample Diamond Lattice


        // Start with a DefaultConfiguration, which comes setup with the
        // most common settings.
        // -------------------------------------------------------------
        Configuration conf = new DefaultConfiguration();

        // Now we need to tell the Configuration object how we want our
        // Chromosomes to be setup. We do that by actually creating a
        // sample Chromosome and then setting it on the Configuration
        // object.
        // --------------------------------------------------------------
        Gene[] sampleGenes = initialize();

        // Set the fitness function we want to use, which is our
        // MinimizingMakeChangeFitnessFunction. We construct it with
        // the target amount of change passed in to this method.
        // ---------------------------------------------------------
        FitnessFunction myFunc =
                new MinDistanceFitness(maxDistance);

        conf.setFitnessFunction(myFunc);

        Chromosome sampleChromosome = new Chromosome(sampleGenes);

        conf.setSampleChromosome(sampleChromosome);

        // Finally, we need to tell the Configuration object how many
        // Chromosomes we want in our population. The more Chromosomes,
        // the larger number of potential solutions (which is good for
        // finding the answer), but the longer it will take to evolve
        // the population (which could be seen as bad). We'll just set
        // the population size to 500 here.
        // ------------------------------------------------------------
        conf.setPopulationSize(500);

        // Create random initial population of Chromosomes.
        // ------------------------------------------------
        Genotype population = Genotype.randomInitialGenotype(conf);

        // Evolve the population. Since we don't know what the best answer
        // is going to be, we just evolve the max number of times.
        // ---------------------------------------------------------------

        runTest(conf, myFunc);

//        Chromosome bestTestSolutionSoFar = null;
        int maxFitnessValue = 0;

        Chromosome bestSolutionSoFar = null;
        for (int i = 0; i < MAX_ALLOWED_EVOLUTIONS; i++) {
            population.evolve();

            if (population.getFittestChromosome().getFitnessValue() > maxFitnessValue) {
                maxFitnessValue = population.getFittestChromosome().getFitnessValue();
                bestSolutionSoFar = (Chromosome) population.getFittestChromosome().clone();
            }
        }

        return getPointsOfSolution(bestSolutionSoFar);

    }

         /**
     *  Description of the Method
     *
     *@param  bestSol  Description of the Parameter
     */
    public static void setLatticeTree(LatticeTree setTree)
    {
        latticeTree = setTree;
        //A new tree so allow for all the restructuring mfunctions to run.
        LatticeFunctions.setIsSet(false);
    }


             /**
     *  Description of the Method
     *
     *@param  bestSol  Description of the Parameter
     */
    public static LatticeTree getLatticeTree()
    {
        return latticeTree;
    }

    /**
     *  Description of the Method
     *
     *@param  bestSol  Description of the Parameter
     */
    public static Object[] graphSolution(Chromosome bestSol) {


        int noOfGenes = bestSol.getGenes().length;
        Object[] xPoints = new Object[noOfGenes];
        Object[] yPoints = new Object[noOfGenes];

        for (int i = (noOfGenes - 1); i > -1; i--) {

            xPoints[i] = new Integer((int) MinDistanceFitness.getDistanceAtGene(bestSol, i));
            yPoints[i] = new Integer(LatticeFunctions.getY(new Integer(i)));
        }
        //Actual graphical functions should be dealt with in the GraphLattice class
        return new Object[]{xPoints, yPoints};
    }


    // -----------------------------------
    // Display the best solution we found.
    // -----------------------------------

    /**
     *  Description of the Method
     *
     *@param  bestSolutionSoFar  Description of the Parameter
     */
    private static Object[] getPointsOfSolution(Chromosome bestSolutionSoFar) {

        try
        {

        //LatticeFunctions.snapToGrid(bestSolutionSoFar.getGenes());
        LatticeFunctions.spaceOutLevels(bestSolutionSoFar.getGenes());

            return graphSolution(bestSolutionSoFar);
        }
            catch (Exception e)
        {
        MraldOutFile.logToFile(Config.getProperty("LOGFILE"),  e.getStackTrace());
        }
        return null;
    }

    /**
     *  All the functionality that is specific to the Lattice should be put in this
     *  method
     *
     *@return    Description of the Return Value
     */
    private static Gene[] initialize() {
        /*
         *  FileInputStream isStream = new FileInputStream("/home/gail/temp.txt");
         *  ObjectInputStream q = new ObjectInputStream(isStream);
         *  LatticeTree newLattice = (LatticeTree)q.readObject();
         */
        //LatticeTree newLattice = SampleLattices.buildSimpleDiamondLattice();
        //LatticeTree newLattice = SampleLattices.buildComplexDiamondLattice();
        //LatticeTree newLattice = SampleLattices.buildVeryComplexDiamondLattice();
        //LatticeTree newLattice = SampleLattices.buildVeryVeryComplexDiamondLattice();
        LatticeTree newLattice = getLatticeTree();

        //System.out.print("MinDist : calcDistance. Lattice " + newLattice.toString() );

        //Set up Lattice
        int noOfNodes = LatticeFunctions.initialize(newLattice.getRootNode());

        maxDistance = LatticeFunctions.calcMaxDistance();

        Gene[] sampleGenes = new Gene[noOfNodes];

        sampleGenes[0] = new IntegerGene(LatticeFunctions.getScreenWidth() /2 , LatticeFunctions.getScreenWidth() /2 + 1);
        // Node

        for (int i = 0; i < noOfNodes - 1; i++)
        {
            sampleGenes[i + 1] = new IntegerGene(0, LatticeFunctions.getScreenWidth());
            // Node
        }
        return sampleGenes;
    }


    /**
     *  Description of the Method
     *
     *@param  conf                               Description of the Parameter
     *@param  myFunc                             Description of the Parameter
     *@exception  InvalidConfigurationException  Description of the Exception
     */
    private static void runTest(Configuration conf, FitnessFunction myFunc)
        throws InvalidConfigurationException {
        // Do a sample test of 5 iterations first.
        // This wil live a ballpark figure for the value of fitness.
        // THis value will be used to narrow the range of fitness values
        // to be between 0 - 2000
        // As larger values result in percentage differences that are
        // too small for the Genetic algorithm to work
        Genotype testPopulation = Genotype.randomInitialGenotype(conf);
        testPopulation.evolve();
        int maxFitnessValue = 0;
        // PM: Written, but never read!
        //Chromosome bestTestSolutionSoFar = null;

        for (int i = 0; i < 5; i++)
        {
            testPopulation.evolve();

            if (testPopulation.getFittestChromosome().getFitnessValue() > maxFitnessValue) {
                maxFitnessValue = testPopulation.getFittestChromosome().getFitnessValue();
                /*bestTestSolutionSoFar = (Chromosome) */
                testPopulation.getFittestChromosome().clone();
            }
        }
        maxDistance = testPopulation.getFittestChromosome().getFitnessValue();
        MinDistanceFitness.setNotTest();
        int testValue = (int) (maxDistance * 0.9);
        MinDistanceFitness.setTestValue(testValue);

    }
}

